package com.geline.mybatisplus.interceptor.inner;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.geline.mybatisplus.annotation.ExtractTable;
import com.geline.mybatisplus.annotation.ExtractTables;
import com.geline.mybatisplus.context.ExtractTableManager;
import com.geline.mybatisplus.context.ExtractTableThreadLocal;
import com.geline.mybatisplus.handler.ForeignKeyHandler;
import com.geline.mybatisplus.interceptor.InnerAfterInterceptor;
import com.geline.mybatisplus.util.MapperUtil;
import com.geline.mybatisplus.util.SpELParserUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.Objects;

/**
 * mybatis-plus主子表状态回填拦截器: @ExtractTable, @ExtractKey, @ExtractStatus
 * 使用方式：interceptor.addInnerInterceptor(new ExtractTableInterceptor());
 * @author mx
 * @date 2025/8/18
 */
@Slf4j
public class ExtractTableInterceptor implements InnerAfterInterceptor {

    private ExtractTableThreadLocal threadLocal;
    private ExtractTableManager manager;
    private ForeignKeyHandler foreignKeyHandler;

    public ExtractTableInterceptor(ExtractTableManager extractTableManager, ForeignKeyHandler foreignKeyHandler){
        this.manager = extractTableManager;
        this.threadLocal = extractTableManager.getThreadLocal();
        this.foreignKeyHandler = foreignKeyHandler;
    }

    @Override
    public boolean willDoUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
        if (ms.getSqlCommandType() == SqlCommandType.DELETE) {//删除之前处理
            if(ms.getId().contains("deleteById")){
                //com.geline.cloud.system.mapper.SysNoticeMapper
                String mapper = ms.getId().replace(".deleteById", "");
                Class<BaseMapper> mapperClass = ClassUtil.loadClass(mapper);
                BaseMapper baseMapper = SpringUtil.getBean(mapperClass);
                Serializable idValue = (Serializable) parameter;
                Object entity = baseMapper.selectById(idValue);
                if(entity!=null){
                    TableName child = entity.getClass().getAnnotation(TableName.class);
                    String childTable = child.value();
                    Class<?> entityClass = entity.getClass();
                    //处理主表(存在 @ExtractKey)
                    ExtractTable[] array = entityClass.getAnnotationsByType(ExtractTable.class);
                    if(array!=null && array.length>0) {
                        for (ExtractTable extractTable : array) {
                            boolean autoFill = extractTable.autoFill();
                            if(!autoFill){
                                continue;
                            }
                            String cond = extractTable.condition();
                            //存在属性值时才执行查询
                            if(StrUtil.isNotBlank(cond)){
                                Boolean execute = SpELParserUtil.executeBoolean(entity, cond);
                                if(!execute){
                                    log.info("@ExtractTable: ({})=false", cond);
                                    return true;
                                }
                            }

                            String foreignKey = extractTable.foreignKey();
                            Serializable mainKey = foreignKeyHandler.getMainKey(entity, extractTable.value(), foreignKey);
                            Objects.requireNonNull(mainKey, String.format("@@ mainKey is null: %s, %s", extractTable.value(), foreignKey));
                            //找到指向的主表
                            TableInfo tableInfo = TableInfoHelper.getTableInfo(extractTable.value());
                            Class<?> entityType = tableInfo.getEntityType();
                            TableName parent = entityType.getAnnotation(TableName.class);
                            String threadKey = parent.value()+":"+entityClass.getName();
                            Object value = threadLocal.getValue(threadKey, mainKey);
                            if(value==null){
                                //主表存在@ExtractKey时，设置到当前线程中
                                if(manager.isExtractKey(entityType) || manager.isExtractCount(entityType) || manager.isExtractSum(entityType)){
                                    Object mainEntity = foreignKeyHandler.getMainEntity(entity, extractTable.value(), foreignKey);
                                    Objects.requireNonNull(mainEntity, String.format("@@ select %s by %s > null", extractTable.value(), foreignKey));
                                    //删除之前处理：取Entity对象并设置到当前线程中
                                    threadLocal.set(threadKey, mainKey, mainEntity, childTable);
                                    log.info("DELETE set @ExtractTable > {}:{}, {}", threadKey, mainKey, mainEntity);
                                }
                            }
                        }
                    }
                }
            }
        }
        return true;
    }

    //添加、更新完成后处理主表
    @Override
    public boolean afterUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
        Object entity = getEntity(ms, parameter);
        if(entity==null){
            Class<?> entityClazz = getEntityClass(ms);
            if(entityClazz!=null){
                ExtractTables[] tables = entityClazz.getAnnotationsByType(ExtractTables.class);
                if(tables!=null && tables.length>0){
                    log.warn("自定义方法[{}]，不能触发@ExtractTable字段回填: {}", ms.getId(), entityClazz.getSimpleName());
                }
            }
            return true;
        }

        manager.handleChildEntity(entity);
        return true;
    }

    private Class getEntityClass(MappedStatement ms) {
        SqlCommandType sct = ms.getSqlCommandType();
        if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.INSERT) {
            String mapperFullClass = ms.getId().substring(0, ms.getId().lastIndexOf("."));
            return MapperUtil.getEntityClass(mapperFullClass);
        }
        return null;
    }

    private Object getEntity(MappedStatement ms, Object parameter) {
        Object entity = null;
        SqlCommandType sct = ms.getSqlCommandType();
        if (sct == SqlCommandType.UPDATE) {
            if(parameter instanceof MapperMethod.ParamMap){
                MapperMethod.ParamMap map = (MapperMethod.ParamMap) parameter;
                if(map.containsKey("et")){
                    entity = map.get("et");
                }
            }
        }if (sct == SqlCommandType.INSERT) {
            entity = parameter;
        }
        return entity;
    }
}
